{
  "nbformat": 4,
  "nbformat_minor": 0,
  "metadata": {
    "colab": {
      "name": "text_classification_with_hub.ipynb",
      "version": "0.3.2",
      "provenance": [],
      "private_outputs": true,
      "collapsed_sections": [],
      "toc_visible": true
    },
    "kernelspec": {
      "display_name": "Python 3",
      "name": "python3"
    }
  },
  "cells": [
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "Ic4_occAAiAT"
      },
      "source": [
        "##### Copyright 2019 The TensorFlow Authors."
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "cellView": "form",
        "colab_type": "code",
        "id": "ioaprt5q5US7",
        "colab": {}
      },
      "source": [
        "#@title Licensed under the Apache License, Version 2.0 (the \"License\");\n",
        "# you may not use this file except in compliance with the License.\n",
        "# You may obtain a copy of the License at\n",
        "#\n",
        "# https://www.apache.org/licenses/LICENSE-2.0\n",
        "#\n",
        "# Unless required by applicable law or agreed to in writing, software\n",
        "# distributed under the License is distributed on an \"AS IS\" BASIS,\n",
        "# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n",
        "# See the License for the specific language governing permissions and\n",
        "# limitations under the License."
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "ItXfxkxvosLH"
      },
      "source": [
        "# Классификация текстов обзоров фильмов с Keras и TensorFlow Hub"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "hKY4XMc9o8iB"
      },
      "source": [
        "<table class=\"tfo-notebook-buttons\" align=\"left\">\n",
        "  <td>\n",
        "    <a target=\"_blank\" href=\"https://www.tensorflow.org/tutorials/keras/text_classification_with_hub\"><img src=\"https://www.tensorflow.org/images/tf_logo_32px.png\" />Смотрите на TensorFlow.org</a>\n",
        "  </td>\n",
        "  <td>\n",
        "    <a target=\"_blank\" href=\"https://colab.research.google.com/github/tensorflow/docs/blob/master/site/ru/tutorials/keras/text_classification_with_hub.ipynb\"><img src=\"https://www.tensorflow.org/images/colab_logo_32px.png\" />Запустите в Google Colab</a>\n",
        "  </td>\n",
        "  <td>\n",
        "    <a target=\"_blank\" href=\"https://github.com/tensorflow/docs/blob/master/site/ru/tutorials/keras/text_classification_with_hub.ipynb\"><img src=\"https://www.tensorflow.org/images/GitHub-Mark-32px.png\" />Изучайте код на GitHub</a>\n",
        "  </td>\n",
        "  <td>\n",
        "    <a href=\"https://storage.googleapis.com/tensorflow_docs/docs/site/ru/tutorials/keras/text_classification_with_hub.ipynb\"><img src=\"https://www.tensorflow.org/images/download_logo_32px.png\" />Скачайте ноутбук</a>\n",
        "  </td>\n",
        "</table>"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "fj66ZXAzrJC2",
        "colab_type": "text"
      },
      "source": [
        "Note: Вся информация в этом разделе переведена с помощью русскоговорящего Tensorflow сообщества на общественных началах. Поскольку этот перевод не является официальным, мы не гарантируем что он на 100% аккуратен и соответствует [официальной документации на английском языке](https://www.tensorflow.org/?hl=en). Если у вас есть предложение как исправить этот перевод, мы будем очень рады увидеть pull request в [tensorflow/docs](https://github.com/tensorflow/docs) репозиторий GitHub. Если вы хотите помочь сделать документацию по Tensorflow лучше (сделать сам перевод или проверить перевод подготовленный кем-то другим), напишите нам на [docs-ru@tensorflow.org list](https://groups.google.com/a/tensorflow.org/forum/#!forum/docs-ru)."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "Eg62Pmz3o83v"
      },
      "source": [
        "\n",
        "В этом уроке мы будем классифицировать обзоры фильмов как *позитивные* или *негативные*, используя текст обзора. Это пример *бинарной* или двуклассовой классификации, важный и широко применяющийся тип задач машинного обучения.\n",
        "\n",
        "Учебное руководство демонстрирует применение переноса обучения (transfer learning) с использованием TensorFlow Hub и Keras.\n",
        "\n",
        "Мы будем использовать [набор данных IMDB](https://www.tensorflow.org/api_docs/python/tf/keras/datasets/imdb) содержащий тексты 50,000 обзоров фильмов из [Internet Movie Database](https://www.imdb.com/). Они разделены на 25,000 обзоров для обучения, и 25,000 обзоров для проверки модели. Обучающая и тестовая выборка *сбалансированы*, т.е. содержат одинаковое количество позитивных и негативных обзоров. \n",
        "\n",
        "Здесь мы будем использовать [tf.keras](https://www.tensorflow.org/guide/keras), высокоуровневый API для построения и обучения моделей в TensorFlow и [TensorFlow Hub](https://www.tensorflow.org/hub), библиотека и платформа для переноса обучения. Для более продвинутого руководства по классификации текстов с использованием `tf.keras`, см. [Руководство по классификации текстов MLCC](https://developers.google.com/machine-learning/guides/text-classification/)."
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "2ew7HTbPpCJH",
        "colab": {}
      },
      "source": [
        "from __future__ import absolute_import, division, print_function, unicode_literals\n",
        "\n",
        "import numpy as np\n",
        "\n",
        "try:\n",
        "  # Colab only\n",
        "  %tensorflow_version 2.x\n",
        "except Exception:\n",
        "    pass\n",
        "import tensorflow as tf\n",
        "\n",
        "import tensorflow_hub as hub\n",
        "import tensorflow_datasets as tfds\n",
        "\n",
        "print(\"Version: \", tf.__version__)\n",
        "print(\"Eager mode: \", tf.executing_eagerly())\n",
        "print(\"Hub version: \", hub.__version__)\n",
        "print(\"GPU is\", \"available\" if tf.config.experimental.list_physical_devices(\"GPU\") else \"NOT AVAILABLE\")"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "iAsKG535pHep"
      },
      "source": [
        "## Скачайте датасет IMDB\n",
        "\n",
        "Датасет IMDB доступен в [датасетах TensorFlow](https://github.com/tensorflow/datasets). Следующий код скачивает датасет IMDB на ваш компьютер (или в среду выполнения Colab):"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "zXXx5Oc3pOmN",
        "colab": {}
      },
      "source": [
        "# Разобьем обучающую выборку в пропорции 60% на 40%, и у нас будет 15,000 примеров\n",
        "# для обучения, 10,000 примеров для валидации и 25,000 примеров для проверки.\n",
        "train_validation_split = tfds.Split.TRAIN.subsplit([6, 4])\n",
        "\n",
        "(train_data, validation_data), test_data = tfds.load(\n",
        "    name=\"imdb_reviews\", \n",
        "    split=(train_validation_split, tfds.Split.TEST),\n",
        "    as_supervised=True)"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "l50X3GfjpU4r"
      },
      "source": [
        "## Исследуйте данные \n",
        "\n",
        "Давайте уделим немного времени, чтобы разобраться в формате данных. Каждый пример представляет собой предложение являющееся обзором фильма и соответствующую метку. Предложение никак не предобработано. Метка является целым числом, 0 или 1, где 0 - это отрицательный отзыв, а 1 - положительный.\n",
        "\n",
        "Выведем первые 10 примеров."
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "QtTS4kpEpjbi",
        "colab": {}
      },
      "source": [
        "train_examples_batch, train_labels_batch = next(iter(train_data.batch(10)))\n",
        "train_examples_batch"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "IFtaCHTdc-GY"
      },
      "source": [
        "Также напечатаем первые 10 меток."
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "tvAjVXOWc6Mj",
        "colab": {}
      },
      "source": [
        "train_labels_batch"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "LLC02j2g-llC"
      },
      "source": [
        "## Постройте модель\n",
        "\n",
        "Нейронная сеть создается послойно - это требует три основных архитектурных решения:\n",
        "\n",
        "* Как представить текст?\n",
        "* Сколько слоев использовать в модели?\n",
        "* Сколько *скрытых нейронов* использовать в каждом слое?\n",
        "\n",
        "В этом примере, входные данные состоят из предложений. Метки которые нужно предсказать являются 0 либо 1.\n",
        "\n",
        "Одним из способов представления текста является преобразование предложений в векторные представления слов. Мы можем использовать предварительно обученное векторное представление текста в качестве первого слоя. Это имеет три преимущества:\n",
        "\n",
        "*   нам не нужно беспокоиться о препроцессинге текста,\n",
        "*   мы можем извлечь выгоду из переноса обучения,\n",
        "*   векторное представление фиксированного размера, поэтому его проще обрабатывать.\n",
        "\n",
        "Для этого примера мы используем **предобученную модель векторного представления текста** из [TensorFlow Hub](https://www.tensorflow.org/hub) называемую [google/tf2-preview/gnews-swivel-20dim/1](https://tfhub.dev/google/tf2-preview/gnews-swivel-20dim/1).\n",
        "\n",
        "Есть еще три другие предварительно обученных модели подходящие для этого руководства:\n",
        "\n",
        "* [google/tf2-preview/gnews-swivel-20dim-with-oov/1](https://tfhub.dev/google/tf2-preview/gnews-swivel-20dim-with-oov/1) - аналогичная [google/tf2-preview/gnews-swivel-20dim/1](https://tfhub.dev/google/tf2-preview/gnews-swivel-20dim/1), но с 2.5% словаря конвертированного в OOV buckets. Это может помочь, если словарь задачи и словарь модели не полностью совпадают.\n",
        "* [google/tf2-preview/nnlm-en-dim50/1](https://tfhub.dev/google/tf2-preview/nnlm-en-dim50/1) - Намного большая модель с размером словаря ~1M и размерностью вложения 50.\n",
        "* [google/tf2-preview/nnlm-en-dim128/1](https://tfhub.dev/google/tf2-preview/nnlm-en-dim128/1) - Еще большая модель с размером словаря ~1M и размерностью вложения 128."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "In2nDpTLkgKa"
      },
      "source": [
        "Давайте сначала создадим слой Keras, который использует модель TensorFlow Hub для векторного представления предложений, и опробуем его на нескольких входных примерах. Обратите внимание, что независимо от длины входного текста, размерность векторного представления будет следующей: `(num_examples, embedding_dimension)`"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "_NUbzVeYkgcO",
        "colab": {}
      },
      "source": [
        "embedding = \"https://tfhub.dev/google/tf2-preview/gnews-swivel-20dim/1\"\n",
        "hub_layer = hub.KerasLayer(embedding, input_shape=[], \n",
        "                           dtype=tf.string, trainable=True)\n",
        "hub_layer(train_examples_batch[:3])"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "dfSbV6igl1EH"
      },
      "source": [
        "Давайте построим полную модель:"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "xpKOoWgu-llD",
        "colab": {}
      },
      "source": [
        "model = tf.keras.Sequential()\n",
        "model.add(hub_layer)\n",
        "model.add(tf.keras.layers.Dense(16, activation='relu'))\n",
        "model.add(tf.keras.layers.Dense(1, activation='sigmoid'))\n",
        "\n",
        "model.summary()"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "6PbKQ6mucuKL"
      },
      "source": [
        "Для построения классификатора зададим слои последовательно:\n",
        "\n",
        "1. Первый слой это слой TensorFlow Hub. Этот слой использует предобученную Saved Model, отображающую предложения в векторные представления. Предобученная модель векторного представления слов которую мы используем ([google/tf2-preview/gnews-swivel-20dim/1](https://tfhub.dev/google/tf2-preview/gnews-swivel-20dim/1)) разбивает предложение на токены, встраивает каждый токен и затем объединяет вложения. В результате получаются размерности: `(num_examples, embedding_dimension)`.\n",
        "2. Получившийся в результате вектор фиксированной длины пропускается сквозь полносвязный (`Dense`) слой состоящий из 16 скрытых нейронов.\n",
        "3. Последний слой плотно связан с единственным выходным нейроном. С использованием функции активации `сигмоида`, значение получается между 0 и 1, представляя вероятность или уровень доверия.\n",
        "\n",
        "Давайте скомпилируем модель."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "L4EqVWg4-llM"
      },
      "source": [
        "### Функция потерь и оптимизатор\n",
        "\n",
        "Для модели нам необходимо указать функцию потерь и оптимизатор для обучения. Поскольку мы решаем задачу бинарной классификации и на выходе модели будут вероятности (слой из единственного элемента с сигмоидой в качестве функции активации), то мы воспользуемся функцией потерь binary_crossentropy (пер. \"Перекрестная энтропия\").\n",
        "\n",
        "Это не единственный выбор для функции потерь: Вы можете, например, выбрать `mean_squared_error`. Но обычно `binary_crossentropy` лучше справляется с вероятностями - она измеряет \"дистанцию\" между распределениями вероятностей, или, как в нашем случае, между истинным распределением и предсказаниями.\n",
        "\n",
        "Далее, когда мы исследуем задачи регрессии (например, предсказание цен на недвижимость), мы посмотрим как использовать другую функцию потерь, которая называется среднеквадратическая ошибка (MSE).\n",
        "\n",
        "А сейчас настроим модель с использованием оптимизатора и функции потерь:"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "Mr0GP-cQ-llN",
        "colab": {}
      },
      "source": [
        "model.compile(optimizer='adam',\n",
        "              loss='binary_crossentropy',\n",
        "              metrics=['accuracy'])"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "35jv_fzP-llU"
      },
      "source": [
        "## Обучите модель\n",
        "\n",
        "Обучите модель за 20 эпох в мини-батчах по 512 образцов. Это 20 итераций по всем образцам данных в тензорах `x_train` и `y_train`. Во время обучениЯ следите за функцией потерь и точностью на 10 000 примеров из проверочного набора данных:"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "tXSGrjWZ-llW",
        "colab": {}
      },
      "source": [
        "history = model.fit(train_data.shuffle(10000).batch(512),\n",
        "                    epochs=20,\n",
        "                    validation_data=validation_data.batch(512),\n",
        "                    verbose=1)"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "9EEGuDVuzb5r"
      },
      "source": [
        "## Оцените модель\n",
        "\n",
        "Давайте посмотрим как работает модель. Она будет возвращать два значения. Потери (число, показывающее нашу ошибку, меньшие значения - лучше) и точность (accuracy)."
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "zOMKywn4zReN",
        "colab": {}
      },
      "source": [
        "results = model.evaluate(test_data.batch(512), verbose=2)\n",
        "for name, value in zip(model.metrics_names, results):\n",
        "  print(\"%s: %.3f\" % (name, value))"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "z1iEXVTR0Z2t"
      },
      "source": [
        "Этот довольно наивный подход достиг точности около 87%. С более продвинутыми методами модель бы приблизилась к 95%."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "5KggXVeL-llZ"
      },
      "source": [
        "## Для дальнейшего чтения\n",
        "\n",
        "Для более общего способа работы со строковыми входными данными и более подробного анализа прогресса точности и потерь во время обучения, взгляните [сюда](https://www.tensorflow.org/tutorials/keras/basic_text_classification)."
      ]
    }
  ]
}
